package com.lagou.ipersistence.builder;

import com.lagou.ipersistence.mapping.ParameterMapping;
import com.lagou.ipersistence.mapping.SqlSource;
import com.lagou.ipersistence.parsing.GenericTokenParser;
import com.lagou.ipersistence.parsing.TokenHandler;
import com.lagou.ipersistence.reflection.MetaClass;
import com.lagou.ipersistence.session.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * {@link com.lagou.ipersistence.mapping.SqlSource} 构建器, 负责将 '#{}' 替换为 ? 占位符,
 * 并获取到该占位符对应的{@link com.lagou.ipersistence.mapping.ParameterMapping}对象
 *
 * @author Yan Jiahong
 * Created on 2020/11/16
 */
public class SqlSourceBuilder extends BaseBuilder {

    public SqlSourceBuilder(Configuration configuration) {
        super(configuration);
    }

    public SqlSource parse(String originSql, Class<?> parameterType) {
        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType);
        GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
        String sql = parser.parse(originSql);
        return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
    }

    private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
        private final List<ParameterMapping> parameterMappings = new ArrayList<>();
        private final Class<?> parameterType;

        public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterTypeClass) {
            super(configuration);
            this.parameterType = parameterTypeClass;
        }

        public List<ParameterMapping> getParameterMappings() {
            return parameterMappings;
        }

        @Override
        public String handle(String token) {
            parameterMappings.add(buildParameterMapping(token));
            return "?";
        }

        /**
         * 将 '#{}'中的内容结合参数类型(parameter type class)构建成一个ParameterMapping对象
         * MyBatis中有一套表达式系统, 但是我们这儿没有
         *
         * @param content 内容
         */
        private ParameterMapping buildParameterMapping(String content) {
            Class<?> propertyType;
            //获取到这个参数MetaClass
            MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
            if (metaClass.hasGetter(content)) {
                propertyType = metaClass.getGetterType(content);
            } else {
                propertyType = Object.class;
            }
            ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, content, propertyType);
            return builder.build();
        }
    }
}
